﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MathLib.Common;
using MBG.Extensions.Core;

namespace MathLib.Algebra
{
    public struct Monomial
    {
        public double Coefficient { get; set; }
        public VariableCollection Variables { get; set; }
        public int Exponent { get; set; }

        public static Monomial Parse(string item)
        {
            Monomial monomial = new Monomial();
            monomial.Variables = new VariableCollection();
            monomial.Coefficient = 1;

            if (string.IsNullOrEmpty(item))
            {
                return monomial;
            }

            double coefficient = 1;
            if (!double.TryParse(item, out coefficient))
            {
                if (CompiledRegex.CoefficientPattern.IsMatch(item))
                {
                    monomial.Coefficient = double.Parse(CompiledRegex.CoefficientPattern.Match(item).Groups["value"].Value);
                }
            }
            else { monomial.Coefficient = coefficient; }

            VariableCollection variables = new VariableCollection();
            foreach (Match match in CompiledRegex.VariableWithExponentPattern.Matches(item))
            {
                variables.Add(Variable.Parse(match.Value));
            }
            monomial.Variables = variables;

            //TODO: Implement exponent for monomial. i.e.: (a2b4)3 <-- 3 = exponent of monomial

            return monomial;
        }

        public static string operator +(Monomial addend1, Monomial addend2)
        {
            return Add(addend1, addend2);
        }
        public static string operator -(Monomial minuend, Monomial subtrahend)
        {
            return Subtract(minuend, subtrahend);
        }
        public static string operator *(Monomial factor1, Monomial factor2)
        {
            return Multiply(factor1, factor2);
        }
        public static string operator /(Monomial dividend, Monomial divisor)
        {
            return Divide(dividend, divisor);
        }

        public static string Add(Monomial addend1, Monomial addend2)
        {
            Monomial answer = new Monomial();
            answer.Variables = new VariableCollection();

            if (addend1.Variables.Equals(addend2.Variables))
            {
                answer.Coefficient = addend1.Coefficient + addend2.Coefficient;
                answer.Variables = addend1.Variables;
            }
            else
            {
                return string.Concat(addend1, " + ", addend2);
            }

            return answer.ToString();
        }
        public static string Subtract(Monomial minuend, Monomial subtrahend)
        {
            Monomial answer = new Monomial();
            answer.Variables = new VariableCollection();

            if (minuend.Variables.Equals(subtrahend.Variables))
            {
                answer.Coefficient = minuend.Coefficient - subtrahend.Coefficient;
                answer.Variables = minuend.Variables;
            }
            else
            {
                return string.Concat(minuend, " - ", subtrahend);
            }

            return answer.ToString();
        }
        public static string Multiply(Monomial factor1, Monomial factor2)
        {
            Monomial answer = new Monomial();
            answer.Variables = new VariableCollection();

            bool foundLikeTerm = false;
            foreach (Variable v1 in factor1.Variables)
            {
                foreach (Variable v2 in factor2.Variables)
                {
                    if (v1.Name == v2.Name)
                    {
                        foundLikeTerm = true;
                        answer.Variables.Add(new Variable(v1.Name, v1.Exponent + v2.Exponent));
                    }
                    if (!foundLikeTerm)
                    {
                        answer.Variables.Add(v2);
                    }
                    foundLikeTerm = false;
                }
            }

            foreach (Variable v in factor1.Variables)
            {
                if (!answer.Variables.ContainsVariableName(v.Name))
                {
                    answer.Variables.Add(v);
                }
            }
            answer.Coefficient = factor1.Coefficient * factor2.Coefficient;

            return answer.ToString();
        }
        public static string Divide(Monomial dividend, Monomial divisor)
        {
            Monomial answer = new Monomial();
            answer.Variables = new VariableCollection();

            bool foundLikeTerm = false;
            foreach (Variable v1 in dividend.Variables)
            {
                foreach (Variable v2 in divisor.Variables)
                {
                    if (v1.Name == v2.Name)
                    {
                        foundLikeTerm = true;
                        answer.Variables.Add(new Variable(v1.Name, v1.Exponent - v2.Exponent));
                    }
                    if (!foundLikeTerm)
                    {
                        answer.Variables.Add(v2);
                    }
                    foundLikeTerm = false;
                }
            }

            foreach (Variable v in dividend.Variables)
            {
                if (!answer.Variables.ContainsVariableName(v.Name))
                {
                    answer.Variables.Add(v);
                }
            }
            answer.Coefficient = dividend.Coefficient / divisor.Coefficient;

            return answer.ToString();
        }

        public override string ToString()
        {
            if (Coefficient == 1)
            {
                return Variables.ToString();
            }
            else
            {
                return string.Concat(Coefficient, Variables);
            }
        }
    }
}